package com.unittec.drainage.provider.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.annotation.Resource;

import com.unittec.drainage.provider.common.Md5;
import com.unittec.drainage.provider.common.ServiceIdConstants;
import com.unittec.drainage.provider.scope.ThreadLocalScope;
import com.unittec.drainage.provider.service.HystrixRestTemplateService;
import com.unittec.drainage.provider.utils.FileUtils;
import com.unittec.drainage.provider.utils.OssUtils;
import com.unittec.drainage.provider.utils.UUIDUtils;
import com.unittec.drainage.provider.utils.ValidationUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.unittec.drainage.provider.log.Log;
import com.unittec.drainage.sdk.auth.request.Req101000;
import com.unittec.drainage.sdk.log.enums.LogOperateEnum;
import com.unittec.drainage.sdk.log.enums.LogStatusEnum;
import com.unittec.drainage.sdk.root.common.CommonConstant;
import com.unittec.drainage.sdk.root.common.FieldConstants;
import com.unittec.drainage.sdk.root.domain.GatewayRequest;
import com.unittec.drainage.sdk.root.domain.Result;
import com.unittec.drainage.sdk.root.enums.BizCode;
import com.unittec.drainage.sdk.root.request.ReqBatchGetUrl;
import com.unittec.drainage.sdk.root.request.ReqGetUrl;
import com.unittec.drainage.sdk.root.response.ResBatchGetUrl;
import com.unittec.drainage.sdk.root.response.ResGetUrl;
import com.unittec.drainage.sdk.root.utils.DateUtiles;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import ws.schild.jave.MultimediaInfo;
import ws.schild.jave.MultimediaObject;

/**
 * @author wuxinwei
 */
@RestController
@Api(tags = "文件上传下载管理")
public class UploadController {

    @Autowired
    Log log;

    @Resource
    private HystrixRestTemplateService hystrixRestTemplateService;

    private static final int DAY = 7;

    @RequestMapping(value = "/uploadByRichText", method = RequestMethod.POST)
    @ApiOperation(value = "文件上传富文本专用", notes = "文件上传富文本专用")
    @ResponseBody
    public String uploadByRichText(@RequestPart(value = "file") MultipartFile file) {
        log.sysInfoLog("uploadByRichText", "文件上传   begin");
        String url = "";
        try {
            String key = fileUpload(file);
            long size = file.getSize();
            String sizeStr = FileUtils.getFileSize(size);
            url = OssUtils.getUrl(key, DAY);
            log.sysInfoLog("uploadByRichText", "文件上传   end,result:" + url);

            Req101000 req = new Req101000();
            String fileName = file.getOriginalFilename();
            req.setFileName(fileName);
            req.setFileExt(FileUtils.getFileExt(fileName));
            req.setFileSize(size);
            req.setFileSizeStr(sizeStr);
            if (FileUtils.getImageType(fileName)) {
                req.setFileType(CommonConstant.FILE_TYPE_0);
            } else {
                req.setFileType(CommonConstant.FILE_TYPE_2);
            }
            req.setOssKey(key);
            req.setOssUrl(url);
            saveFile(req);
            return url;
        } catch (Exception e) {
            log.sysErrLog("uploadByRichText", "文件上传失败", e);
            return "";
        }

    }

    @RequestMapping(value = "/uploadVideo", method = RequestMethod.POST)
    @ApiOperation(value = "上传视频", notes = "上传视频")
    @ResponseBody
    public JSONObject uploadVideo(@RequestPart(value = "file") MultipartFile file) {
        JSONObject res = new JSONObject();
        log.sysInfoLog("uploadVideo", "文件上传   begin");
        String originalFilename = file.getOriginalFilename();

        if (StringUtils.isNotBlank(originalFilename)) {
            if (FileUtils.checkSpecialStr(originalFilename)) {
                res.put("code", 200);
                res.put("biz_code", "500");
                res.put("biz_msg", "上传文件名不能包含特殊字符");
                return res;
            }
        }

        try {
            String key = fileUpload(file);
            File tempFile = multipartFileToFile(file);
            String time = readVideoTime(tempFile);
            long size = file.getSize();
            String sizeStr = FileUtils.getFileSize(size);
            String videoLength = readVideoSize(tempFile);

            res.put("code", 200);
            // res.put("msg", "");
            res.put("biz_code", "200");
            // res.put("biz_msg", "");

            String url = OssUtils.getUrl(key, DAY);
            log.sysInfoLog("uploadVideo", "上传视频   end,result:" + url);

            JSONObject data = new JSONObject();
            data.put("fileName", originalFilename);
            // data.put("key", key);
            data.put("url", url);
            data.put("time", time);
            data.put("size", sizeStr);
            data.put("videoLength", videoLength);

            res.put("data", data);

            Req101000 req = new Req101000();
            String fileName = file.getOriginalFilename();
            req.setFileName(fileName);
            req.setFileExt(FileUtils.getFileExt(fileName));
            req.setFileSize(size);
            req.setFileSizeStr(sizeStr);
            req.setFileType(CommonConstant.FILE_TYPE_1);
            req.setOssKey(key);
            req.setOssUrl(url);
            req.setVideoLength(videoLength);
            saveFile(req);

            return res;
        } catch (Exception e) {
            log.sysErrLog("uploadVideo", "上传视频失败", e);
            res.put("code", 200);
            // res.put("msg", "");
            res.put("biz_code", "500");
            res.put("biz_msg", "上传视频失败");
            res.put("msg", "上传失败");
            return res;
        }

    }

    public static File multipartFileToFile(MultipartFile file) throws Exception {

        File toFile = null;
        if (null == file || file.getSize() <= 0) {
            file = null;
        } else {
            InputStream ins = null;
            ins = file.getInputStream();
            toFile = new File(file.getOriginalFilename());
            inputStreamToFile(ins, toFile);
            ins.close();
        }
        return toFile;
    }

    // 获取流文件
    private static void inputStreamToFile(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除本地临时文件
     *
     * @param file
     */
    public static void delteTempFile(File file) {
        if (file != null) {
            File del = new File(file.toURI());
            del.delete();
        }
    }

    /**
     * 获取视频时长
     *
     * @param source
     * @return
     */
    private String readVideoTime(File source) {
        String length = "";
        try {
            MultimediaObject instance = new MultimediaObject(source);
            MultimediaInfo result = instance.getInfo();
            long ls = result.getDuration() / 1000;
            int hour = (int)(ls / 3600);
            int minute = (int)(ls % 3600) / 60;
            int second = (int)(ls - hour * 3600 - minute * 60);
            length = hour + "'" + minute + "''" + second + "'''";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return length;
    }

    /**
     * 获取视频大小
     *
     * @param source
     * @return
     */
    private String readVideoSize(File source) {
        FileChannel fc = null;
        String size = "";
        try {
            @SuppressWarnings("resource")
            FileInputStream fis = new FileInputStream(source);
            fc = fis.getChannel();
            BigDecimal fileSize = new BigDecimal(fc.size());
            size = fileSize.divide(new BigDecimal(1048576), 2, RoundingMode.HALF_UP) + "MB";
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != fc) {
                try {
                    fc.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return size;
    }

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ApiOperation(value = "文件上传", notes = "文件上传")
    @ResponseBody
    public JSONObject upload(@RequestPart(value = "file") MultipartFile file) {
        log.sysInfoLog("upload", "文件上传   begin");
        JSONObject res = new JSONObject();

        String originalFilename = file.getOriginalFilename();

        if (StringUtils.isNotBlank(originalFilename)) {
            if (FileUtils.checkSpecialStr(originalFilename)) {
                res.put("code", 200);
                res.put("msg", "请求成功");
                res.put("biz_code", "500");
                res.put("biz_msg", "上传文件名不能包含特殊字符");
                return res;
            }
        }

        try {
            String key = fileUpload(file);
            long size = file.getSize();
            String sizeStr = FileUtils.getFileSize(size);
            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "200");
            res.put("biz_msg", "业务处理成功");

            String url = OssUtils.getUrl(key, DAY);

            JSONObject data = new JSONObject();
            data.put("fileName", originalFilename);
            data.put("key", key);
            data.put("url", url);
            data.put("size", sizeStr);
            res.put("data", data);

            Req101000 req = new Req101000();
            String fileName = file.getOriginalFilename();
            req.setFileName(fileName);
            req.setFileExt(FileUtils.getFileExt(fileName));
            req.setFileSize(size);
            req.setFileSizeStr(sizeStr);
            if (FileUtils.getImageType(fileName)) {
                req.setFileType(CommonConstant.FILE_TYPE_0);
            } else if (FileUtils.getImageType(fileName)) {
                req.setFileType(CommonConstant.FILE_TYPE_1);
            } else {
                req.setFileType(CommonConstant.FILE_TYPE_2);
            }
            req.setOssKey(key);
            req.setOssUrl(url);
            saveFile(req);

        } catch (Exception e) {
            log.sysErrLog("upload", "文件上传失败", e);
            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "500");
            res.put("biz_msg", "上传文件失败");
        }
        log.sysInfoLog("upload", "文件上传   end,result:" + res.toJSONString());
        return res;
    }

    @RequestMapping(value = "/fileUpload", method = RequestMethod.POST)
    @ApiOperation(value = "文件上传", notes = "文件上传")
    @ResponseBody
    public JSONObject uploadFile(@RequestPart(value = "file") MultipartFile file) {
        log.sysInfoLog("uploadFile", "文件上传   begin");
        JSONObject res = new JSONObject();
        File tempFile = null;
        try {
            String key = fileUpload(file);
            tempFile = multipartFileToFile(file);
            String originalFilename = file.getOriginalFilename();
            long size = file.getSize();
            String sizeStr = FileUtils.getFileSize(size);
            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "200");
            res.put("biz_msg", "业务处理成功");

            String url = OssUtils.getUrl(key, DAY);

            JSONObject data = new JSONObject();
            data.put("file_name", originalFilename);
            data.put("key", key);
            data.put("url", url);
            data.put("file_size", sizeStr);
            if (FileUtils.getImageType(originalFilename)) {
                data.put("file_type", CommonConstant.FILE_TYPE_0);
            } else if (FileUtils.getImageType(originalFilename)) {
                data.put("file_type", CommonConstant.FILE_TYPE_1);
            } else {
                data.put("file_type", CommonConstant.FILE_TYPE_2);
            }
            res.put("data", data);
        } catch (Exception e) {
            log.sysErrLog("uploadFile", "文件上传失败", e);

            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "500");
            res.put("biz_msg", "上传文件失败" + e.getMessage());
        } finally {
            if (tempFile != null) {
                tempFile.delete();
            }
        }
        log.sysInfoLog("uploadFile", "文件上传   end,result:" + res.toJSONString());
        return res;
    }

    @RequestMapping(value = "/bacthUpload", method = RequestMethod.POST)
    @ApiOperation(value = "批量文件上传", notes = "批量文件上传")
    @ResponseBody
    public JSONObject bacthUpload(MultipartFile[] files) {
        log.sysInfoLog("bacthUpload", "文件上传   begin");
        JSONObject res = new JSONObject();
        try {
            JSONArray array = new JSONArray();
            for (MultipartFile file : files) {
                String key = fileUpload(file);
                String url = OssUtils.getUrl(key, DAY);
                String originalFilename = file.getOriginalFilename();

                if (StringUtils.isNotBlank(originalFilename)) {
                    if (FileUtils.checkSpecialStr(originalFilename)) {
                        res.put("code", 200);
                        res.put("msg", "请求成功");
                        res.put("biz_code", "500");
                        res.put("biz_msg", "上传文件名不能包含特殊字符");
                        return res;
                    }
                }

                JSONObject fileObject = new JSONObject();

                long size = file.getSize();
                String sizeStr = FileUtils.getFileSize(size);
                fileObject.put("fileName", originalFilename);
                fileObject.put("key", key);
                fileObject.put("url", url);
                fileObject.put("size", sizeStr);
                array.add(fileObject);

                Req101000 req = new Req101000();
                String fileName = file.getOriginalFilename();
                req.setFileName(fileName);
                req.setFileExt(FileUtils.getFileExt(fileName));
                req.setFileSize(size);
                req.setFileSizeStr(sizeStr);
                if (FileUtils.getImageType(fileName)) {
                    req.setFileType(CommonConstant.FILE_TYPE_0);
                } else {
                    req.setFileType(CommonConstant.FILE_TYPE_2);
                }
                req.setOssKey(key);
                req.setOssUrl(url);
                saveFile(req);

            }

            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "200");
            res.put("data", array);
        } catch (Exception e) {
            log.sysErrLog("upload", "文件上传失败", e);
            res.put("code", 200);
            res.put("msg", "请求成功");
            res.put("biz_code", "500");
            res.put("biz_msg", "上传文件失败");
        }
        log.sysInfoLog("bacthUpload", "文件上传   end,result:" + res.toJSONString());
        return res;
    }

    /**
     * 文件上传OSS
     *
     * @param file
     * @return
     */
    private String fileUpload(MultipartFile file) throws Exception {
        log.sysInfoLog("fileUpload", "上传文件到OSS begin");
        String path = DateUtiles.format(new Date(), "yyyy/MM/dd");
        // 原始文件名
        String originalFilename = file.getOriginalFilename();

        // 获取文件名
        String fileName = file.getOriginalFilename();
        // 获取文件后缀名
        String suffixName = "";
        if (fileName.contains(".")) {
            suffixName = fileName.substring(fileName.lastIndexOf("."));
        }
        // 重新生成文件名
        fileName = UUID.randomUUID() + suffixName;

        String key = path + "/" + UUIDUtils.getId() + suffixName;

        try {
            final long partSize = 20 * 1024 * 1024L;
            // 文件大于5M分片上传
            if (file.getSize() > partSize) {
                //OssUtils.uploadObject2OssForBurst(key, file);
                OssUtils.uploadObject2Oss(key, originalFilename, file.getInputStream());//
            } else {
                OssUtils.uploadObject2Oss(key, originalFilename, file.getInputStream());
            }
        } catch (Exception e) {
            log.sysErrLog("fileUpload", "文件上传OSS失败", e);
            throw new Exception("文件上传OSS失败" + e.getMessage());
        }
        log.sysInfoLog("fileUpload", "上传文件到OSS end,key:" + key);
        return key;
    }


    @RequestMapping(value = "/getUrl", method = RequestMethod.POST)
    @ApiOperation(value = "获取文件url", notes = "获取文件url")
    @ResponseBody
    public Result<ResGetUrl> getUrl(@RequestBody GatewayRequest<ReqGetUrl> req) {
        log.debug(req, "getUrl  {},param:{}  ", "begin", JSON.toJSONString(req.getContent()));
        long startLogTime = System.currentTimeMillis();
        try {
            // 参数校验
            ValidationUtils.validateEntityStr(req.getContent());
        } catch (Exception e) {
            log.error(req, "getUrl  error  end ", e);
            log.addOperateLog("getUrl", JSON.toJSONString(req.getContent()), "获取文件url", "参数错误：" + log.buildErrmsg(e),
                LogOperateEnum.LOG_ADD.getCode(), LogStatusEnum.ERROR.getCode(), startLogTime);
            return Result.error(req, BizCode.PARAM_ERROR.getCode(), e.getMessage());
        }

        try {
            String url = OssUtils.getUrl(req.getContent().getUrlKey(), DAY);
            ResGetUrl res = new ResGetUrl();
            res.setUrl(url);
            log.debug(req, "getUrl  success  end ");
            return Result.success(req, res);
        } catch (Exception e) {
            log.error(req, "getUrl  error  end ", e);
            log.addOperateLog("getUrl", JSON.toJSONString(req.getContent()), "获取文件url", "获取图片错误：" + log.buildErrmsg(e),
                LogOperateEnum.LOG_ADD.getCode(), LogStatusEnum.ERROR.getCode(), startLogTime);
            return Result.error(req, BizCode.QUERY_ERROR.getCode(), e.getMessage());
        }
    }

    @RequestMapping(value = "/batchGetUrl", method = RequestMethod.POST)
    @ApiOperation(value = "批量获取文件url", notes = "批量获取文件url")
    @ResponseBody
    public Result<ResBatchGetUrl> batchGetUrl(@RequestBody GatewayRequest<ReqBatchGetUrl> req) {
        log.debug(req, "batchGetUrl  {},param:{}  ", "begin", JSON.toJSONString(req.getContent()));
        long startLogTime = System.currentTimeMillis();
        try {
            // 参数校验
            ValidationUtils.validateEntityStr(req.getContent());
        } catch (Exception e) {
            log.error(req, "batchGetUrl  error  end ", e);
            log.addOperateLog("batchGetUrl", JSON.toJSONString(req.getContent()), "批量获取文件url",
                "参数错误：" + log.buildErrmsg(e), LogOperateEnum.LOG_ADD.getCode(), LogStatusEnum.ERROR.getCode(),
                startLogTime);
            return Result.error(req, BizCode.PARAM_ERROR.getCode(), e.getMessage());
        }

        try {
            List<String> urlPaths = new ArrayList<>();
            JSONArray array = new JSONArray();
            for (String key : req.getContent().getUrlKeyList()) {
                String url = OssUtils.getUrl(key, DAY);
                urlPaths.add(url);

                JSONObject fileObject = new JSONObject();
                fileObject.put("key", key);
                fileObject.put("url", url);
                array.add(fileObject);
            }
            ResBatchGetUrl res = new ResBatchGetUrl();
            res.setUrlPaths(urlPaths);
            res.setArray(array);
            log.debug(req, "batchGetUrl  success  end ");
            return Result.success(req, res);
        } catch (Exception e) {
            log.error(req, "batchGetUrl  error  end ", e);
            log.addOperateLog("batchGetUrl", JSON.toJSONString(req.getContent()), "批量获取文件url",
                "获取图片错误：" + log.buildErrmsg(e), LogOperateEnum.LOG_ADD.getCode(), LogStatusEnum.ERROR.getCode(),
                startLogTime);
            return Result.error(req, BizCode.QUERY_ERROR.getCode(), e.getMessage());
        }
    }

    private void saveFile(Req101000 req) {
        GatewayRequest<Req101000> getWay = new GatewayRequest<>();
        try {
            getWay.setContent(req);
            Result<Object> result = hystrixRestTemplateService.exchange(
                Md5.getMd5(FieldConstants.SYS_REQUEST_SOURCE_ID), ServiceIdConstants.AUTH_101000, getWay,
                ThreadLocalScope.getAuthData(), new ParameterizedTypeReference<Result<Object>>() {});
        } catch (RuntimeException e) {
            // 捕获到异常后，在本地记录日志，确保日志信息不会丢失
            log.error(getWay, "saveFile  error  end ", e);
        }
    }

}
